﻿namespace TrinityEngine
{
    public struct Vector3Int : IEquatable<Vector3Int>
    {
        public int x;

        public int y;

        public int z;

        public float sqrMagnitude
        {
            get
            {
                return x * x + y * y + z * z;
            }
        }

        public float magnitude
        {
            get
            {
                return Mathf.Sqrt(sqrMagnitude);
            }
        }

        public Vector3Int(int x, int y, int z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        public readonly static Vector3Int zero = new Vector3Int(0, 0, 0);

        public readonly static Vector3Int one = new Vector3Int(1, 1, 0);

        public readonly static Vector3Int up = new Vector3Int(0, 1, 0);

        public readonly static Vector3Int down = new Vector3Int(0, -1, 0);

        public readonly static Vector3Int left = new Vector3Int(-1, 0, 0);

        public readonly static Vector3Int right = new Vector3Int(1, 0, 0);

        public readonly static Vector3Int forward = new Vector3Int(0, 0, 1);

        public readonly static Vector3Int back = new Vector3Int(0, 0, -1);

        public Vector2Int ToVector2Int()
        {
            return new Vector2Int(x, y);
        }

        public static Vector3Int operator -(Vector3Int a)
        {
            return new Vector3Int(-a.x, -a.y, -a.z);
        }

        public static Vector3Int operator +(Vector3Int a, Vector3Int b)
        {
            return new Vector3Int(a.x + b.x, a.y + b.y, a.z + b.z);
        }

        public static Vector3Int operator -(Vector3Int a, Vector3Int b)
        {
            return new Vector3Int(a.x - b.x, a.y - b.y, a.z - b.z);
        }

        public static Vector3Int operator *(Vector3Int a, Vector3Int b)
        {
            return new Vector3Int(a.x * b.x, a.y * b.y, a.z * b.z);
        }

        public static Vector3Int operator *(Vector3Int a, int b)
        {
            return new Vector3Int(a.x * b, a.y * b, a.z * b);
        }

        public static Vector3Int operator *(int a, Vector3Int b)
        {
            return b * a;
        }

        public static Vector3 operator *(Vector3Int a, float b)
        {
            return new Vector3(a.x * b, a.y * b, a.z * b);
        }

        public static Vector3 operator *(float a, Vector3Int b)
        {
            return b * a;
        }

        public static Vector3Int operator /(Vector3Int a, Vector3Int b)
        {
            if (b.x * b.y * b.z == 0)
            {
                throw new Exception("Dividend could not be zero!");
            }

            return new Vector3Int(a.x / b.x, a.y / b.y, a.z / b.z);
        }

        public static Vector3Int operator /(Vector3Int a, int b)
        {
            if (b == 0)
            {
                throw new Exception("Dividend could not be zero!");
            }

            return new Vector3Int(a.x / b, a.y / b, a.z / b);
        }

        public static Vector3Int operator /(int a, Vector3Int b)
        {
            return b / a;
        }

        public bool Equals(Vector3Int other)
        {
            return x == other.x && y == other.y && z == other.z;
        }

        public override bool Equals(object obj)
        {
            if (obj is Vector3Int v)
            {
                return Equals(v);
            }

            return false;
        }

        public override int GetHashCode()
        {
            return x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode();
        }

        public override string ToString()
        {
            return $"Vector3Int({x}, {y}, {z})";
        }

        public static bool operator ==(Vector3Int a, Vector3Int b)
        {
            return a.Equals(b);
        }

        public static bool operator !=(Vector3Int a, Vector3Int b)
        {
            return !a.Equals(b);
        }

        public static implicit operator OpenTK.Mathematics.Vector3i(Vector3Int a)
        {
            return new OpenTK.Mathematics.Vector3i(a.x, a.y, a.z);
        }
    }
}
